package cn.ryh.netty.nio;


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

/**
 * 服务端和客户端连接示例
 *
 * @author Raoyh
 */
public class NioServerDemo {
    public static void main(String[] args) throws IOException {
        server();
    }

    public static void server() throws IOException {
        //获取通道  创建ServerSocketChannel -> ServerSocket
        try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
            //切换为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            //绑定连接
            serverSocketChannel.bind(new InetSocketAddress(9999));
            //获取选择器Selector对象
            Selector selector = Selector.open();
            //将通道注册到选择器上,并且指定"监听接收事件"
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            //轮询式的获取选择器上已经"准备就绪"的事件
            while (true) {
                //这里我们等待1秒，如果没有事件发生, 返回
                //没有事件发生
                if (selector.select(1000) == 0) {
                    System.out.println("服务器等待了1秒，无连接");
                    continue;
                }
                //获取当前选择器中所有注册的"选择键(已就绪的监听事件)"
                Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                while (it.hasNext()) {
                    //获取准备就绪的事件
                    SelectionKey key = it.next();
                    //判断具体是什么事件准备就绪
                    //如果是 OP_ACCEPT, 有新的客户端连接
                    if (key.isAcceptable()) {
                        //若接收就绪,获取客户端连接
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        System.out.println("客户端连接成功 生成了一个 socketChannel " + socketChannel.hashCode());
                        //将  SocketChannel 设置为非阻塞
                        socketChannel.configureBlocking(false);
                        //将该通道注册到选择器上, 同时给socketChannel关联一个Buffer
                        socketChannel.register(selector, SelectionKey.OP_READ,ByteBuffer.allocate(1024));
                    }
                    //发生 OP_READ
                    if (key.isReadable()) {
                        //获取当前选择器上"读就绪"状态的通道
                        try (SocketChannel channel = (SocketChannel) key.channel()) {
                            //获取到该channel关联的buffer
                            ByteBuffer buffer = (ByteBuffer)key.attachment();
                            channel.read(buffer);
                            System.out.println("form 客户端 " + new String(buffer.array(), StandardCharsets.UTF_8));
                        }
                    }
                    //手动从集合中移动当前的selectionKey, 防止重复操作
                    it.remove();
                }
            }
        }
    }

}

